package org.openintents.filemanager.util;

import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;

import androidx.fragment.app.DialogFragment;

import org.openintents.filemanager.FileManagerActivity;
import org.openintents.filemanager.FileManagerApplication;
import org.openintents.filemanager.FileManagerProvider;
import org.openintents.filemanager.PreferenceFragment;
import org.openintents.filemanager.R;
import org.openintents.filemanager.bookmarks.BookmarksProvider;
import org.openintents.filemanager.dialogs.DetailsDialog;
import org.openintents.filemanager.dialogs.MultiCompressDialog;
import org.openintents.filemanager.dialogs.MultiDeleteDialog;
import org.openintents.filemanager.dialogs.RenameDialog;
import org.openintents.filemanager.dialogs.SingleCompressDialog;
import org.openintents.filemanager.dialogs.SingleDeleteDialog;
import org.openintents.filemanager.files.FileHolder;
import org.openintents.filemanager.lists.FileListFragment;
import org.openintents.filemanager.lists.SimpleFileListFragment;
import org.openintents.intents.FileManagerIntents;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Utility class that helps centralize multiple and single selection actions for all API levels.
 *
 * @author George Venios
 */
public abstract class MenuUtils {
    /**
     * Fill <code>m</code> with multiselection actions, using <code>mi</code>.
     *
     * @param m            The {@link Menu} to fill.
     * @param menuResource The resource id of the menu to load.
     * @param mi           The {@link MenuInflater} to use. This is a parameter since the ActionMode provides a context-based inflater and we could possibly lose functionality with a common MenuInflater.
     */
    static public void fillMultiselectionMenu(Menu m, int menuResource, MenuInflater mi) {
        mi.inflate(menuResource, m);
    }

    /**
     * Fill the passed Menu attribute with the proper single selection actions for the passed {@link FileHolder} object.
     *
     * @param m            The {@link Menu} to fill.
     * @param menuResource The resource id of the menu to load.
     * @param mi           The {@link MenuInflater} to use. This is a parameter since the ActionMode provides a context-based inflater and we could possibly lose functionality with a common MenuInflater.
     */
    static public void fillContextMenu(FileHolder item, Menu m, int menuResource, MenuInflater mi, Context context) {
        // Inflate all actions
        mi.inflate(menuResource, m);

        if (m instanceof ContextMenu) {
            ((ContextMenu) m).setHeaderTitle(item.getName());
            ((ContextMenu) m).setHeaderIcon(item.getIcon());
        }

        // Get the selected file
        File file = item.getFile();

        // If selected item is a directory
        if (item.isDirectory() != null && item.isDirectory()) {
            m.removeItem(R.id.menu_send);
        }

        // If selected item is a zip archive
        if (!FileUtils.checkIfZipArchive(file)) {
            m.removeItem(R.id.menu_extract);
        } else {
            m.removeItem(R.id.menu_compress);
        }

        // Add CATEGORY_SELECTED_ALTERNATIVE intent options
        Uri data = Uri.fromFile(file);
        Intent intent = new Intent(null, data);

        intent.setDataAndType(data, item.getMimeType());
        intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);

        if (item.getMimeType() != null) {
            // Add additional options for the MIME type of the selected file.
            m.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
                    new ComponentName(context, FileManagerActivity.class), null, intent, 0, null);
        }
    }

    /**
     * Central point where we handle actions for multiple selection, for every API level.
     *
     * @param mItem  The selected menu option/action.
     * @param fItems The data to act upon.
     */
    public static boolean handleMultipleSelectionAction(final FileListFragment navigator, MenuItem mItem, List<FileHolder> fItems, Context context) {
        DialogFragment dialog;
        Bundle args;

        switch (mItem.getItemId()) {
            case R.id.menu_send:
                Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
                ArrayList<Uri> uris = new ArrayList<>();
                intent.setType("text/plain");

                for (FileHolder fh : fItems)
                    uris.add(FileManagerProvider.getUriForFile(fh.getFile().getAbsolutePath()));

                intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);

                context.startActivity(Intent.createChooser(intent, context.getString(R.string.send_chooser_title)));
                break;
            case R.id.menu_delete:
                dialog = new MultiDeleteDialog();
                dialog.setTargetFragment(navigator, 0);
                args = new Bundle();
                args.putParcelableArrayList(FileManagerIntents.EXTRA_DIALOG_FILE_HOLDER, new ArrayList<Parcelable>(fItems));
                dialog.setArguments(args);
                dialog.show(navigator.getFragmentManager(), MultiDeleteDialog.class.getName());
                break;
            case R.id.menu_move:
                ((FileManagerApplication) navigator.getActivity().getApplication()).getCopyHelper().cut(fItems);
                navigator.updateClipboardInfo();

                // Refresh options menu
                navigator.getActivity().supportInvalidateOptionsMenu();
                break;
            case R.id.menu_copy:
                ((FileManagerApplication) navigator.getActivity().getApplication()).getCopyHelper().copy(fItems);
                navigator.updateClipboardInfo();

                // Refresh options menu
                navigator.getActivity().supportInvalidateOptionsMenu();
                break;
            case R.id.menu_compress:
                dialog = new MultiCompressDialog();
                dialog.setTargetFragment(navigator, 0);
                args = new Bundle();
                args.putParcelableArrayList(FileManagerIntents.EXTRA_DIALOG_FILE_HOLDER, new ArrayList<Parcelable>(fItems));
                dialog.setArguments(args);
                dialog.show(navigator.getFragmentManager(), MultiCompressDialog.class.getName());
                break;
            default:
                return false;
        }

        return true;
    }

    /**
     * Central point where we handle actions for single selection, for every API level.
     *
     * @param mItem The selected menu option/action.
     * @param fItem The data to act upon.
     */
    public static boolean handleSingleSelectionAction(final SimpleFileListFragment navigator, MenuItem mItem, FileHolder fItem, Context context) {
        DialogFragment dialog;
        Bundle args;

        switch (mItem.getItemId()) {
            case R.id.menu_open:
                navigator.openInformingPathBar(fItem);
                return true;

            case R.id.menu_create_shortcut:
                createShortcut(fItem, context);
                return true;

            case R.id.menu_move:
                ((FileManagerApplication) navigator.getActivity().getApplication()).getCopyHelper().cut(fItem);
                navigator.updateClipboardInfo();

                // Refresh options menu
                navigator.getActivity().supportInvalidateOptionsMenu();
                return true;

            case R.id.menu_copy:
                ((FileManagerApplication) navigator.getActivity().getApplication()).getCopyHelper().copy(fItem);
                navigator.updateClipboardInfo();

                // Refresh options menu
                navigator.getActivity().supportInvalidateOptionsMenu();
                return true;

            case R.id.menu_delete:
                dialog = new SingleDeleteDialog();
                dialog.setTargetFragment(navigator, 0);
                args = new Bundle();
                args.putParcelable(FileManagerIntents.EXTRA_DIALOG_FILE_HOLDER, fItem);
                dialog.setArguments(args);
                dialog.show(navigator.getFragmentManager(), SingleDeleteDialog.class.getName());
                return true;

            case R.id.menu_rename:
                dialog = new RenameDialog();
                dialog.setTargetFragment(navigator, 0);
                args = new Bundle();
                args.putParcelable(FileManagerIntents.EXTRA_DIALOG_FILE_HOLDER, fItem);
                dialog.setArguments(args);
                dialog.show(navigator.getFragmentManager(), RenameDialog.class.getName());
                return true;

            case R.id.menu_send:
                sendFile(fItem, context);
                return true;

            case R.id.menu_details:
                dialog = new DetailsDialog();
                dialog.setTargetFragment(navigator, 0);
                args = new Bundle();
                args.putParcelable(FileManagerIntents.EXTRA_DIALOG_FILE_HOLDER, fItem);
                dialog.setArguments(args);
                dialog.show(navigator.getFragmentManager(), DetailsDialog.class.getName());
                return true;

            case R.id.menu_compress:
                dialog = new SingleCompressDialog();
                dialog.setTargetFragment(navigator, 0);
                args = new Bundle();
                args.putParcelable(FileManagerIntents.EXTRA_DIALOG_FILE_HOLDER, fItem);
                dialog.setArguments(args);
                dialog.show(navigator.getFragmentManager(), SingleCompressDialog.class.getName());
                return true;

            case R.id.menu_extract:
                File dest = new File(fItem.getFile().getParentFile(), FileUtils.getNameWithoutExtension(fItem.getFile()));
                dest.mkdirs();

                // Changed from the previous behavior.
                // We just extract on the current directory. If the user needs to put it in another dir,
                // he/she can copy/cut the file with the new, equally easy to use way.
                new ExtractManager(context)
                        .setOnExtractFinishedListener(new ExtractManager.OnExtractFinishedListener() {

                            @Override
                            public void extractFinished() {
                                navigator.refresh();
                            }
                        })
                        .extract(fItem.getFile(), dest.getAbsolutePath());
                return true;

            case R.id.menu_bookmark:
                String path = fItem.getFile().getAbsolutePath();
                Cursor query = context.getContentResolver().query(BookmarksProvider.CONTENT_URI,
                        new String[]{BookmarksProvider._ID},
                        BookmarksProvider.PATH + "=?",
                        new String[]{path},
                        null);
                if (!query.moveToFirst()) {
                    ContentValues values = new ContentValues();
                    values.put(BookmarksProvider.NAME, fItem.getName());
                    values.put(BookmarksProvider.PATH, path);
                    context.getContentResolver().insert(BookmarksProvider.CONTENT_URI, values);
                    Toast.makeText(context, R.string.bookmark_added, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(context, R.string.bookmark_already_exists, Toast.LENGTH_SHORT).show();
                }
                query.close();
                return true;

            case R.id.menu_more:
                if (!PreferenceFragment.getShowAllWarning(context)) {
                    showMoreCommandsDialog(fItem, context);
                    return true;
                }
                showWarningDialog(fItem, context);

                return true;
        }

        return false;
    }

    /**
     * Creates a home screen shortcut.
     *
     * @param fileholder The {@link File} to create the shortcut to.
     */
    static private void createShortcut(FileHolder fileholder, Context context) {
        Intent shortcutintent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
        shortcutintent.putExtra("duplicate", false);
        shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_NAME, fileholder.getName());
        Parcelable icon = Intent.ShortcutIconResource.fromContext(context.getApplicationContext(), R.drawable.ic_launcher_shortcut);
        shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);

        // Intent to load
        Intent itl = new Intent(Intent.ACTION_VIEW);
        if (fileholder.getFile().isDirectory())
            itl.setData(Uri.fromFile(fileholder.getFile()));
        else
            itl.setDataAndType(Uri.fromFile(fileholder.getFile()), fileholder.getMimeType());
        itl.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        shortcutintent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, itl);
        context.sendBroadcast(shortcutintent);
    }

    /**
     * Creates an activity picker to send a file.
     *
     * @param fHolder A {@link FileHolder} containing the {@link File} to send.
     * @param context {@link Context} in which to create the picker.
     */
    private static void sendFile(FileHolder fHolder, Context context) {
        String filename = fHolder.getName();

        Intent i = new Intent();
        i.setAction(Intent.ACTION_SEND);
        i.setType(fHolder.getMimeType());
        i.putExtra(Intent.EXTRA_SUBJECT, filename);
        i.putExtra(Intent.EXTRA_STREAM, FileManagerProvider.getUriForFile(fHolder.getFile().getAbsolutePath()));

        i = Intent.createChooser(i, context.getString(R.string.menu_send));

        try {
            context.startActivity(i);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(context, R.string.send_not_available, Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Call this to show the dialog that informs the user about possibly broken options in the "More" dialog.
     *
     * @param context The context that will be used for this dialog.
     * @param holder  A {@link FileHolder} containing the file to act upon.
     */
    private static void showWarningDialog(final FileHolder holder, final Context context) {
        LayoutInflater li = LayoutInflater.from(context);
        View warningView = li.inflate(R.layout.dialog_warning, null);
        final CheckBox showWarningAgain = warningView.findViewById(R.id.showagaincheckbox);

        showWarningAgain.setChecked(PreferenceFragment.getShowAllWarning(context));

        new AlertDialog.Builder(context).setView(warningView).setTitle(context.getString(R.string.title_warning_some_may_not_work))
                .setMessage(context.getString(R.string.warning_some_may_not_work))
                .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(
                android.R.string.ok, new OnClickListener() {

                    public void onClick(DialogInterface dialog, int which) {
                        PreferenceFragment.setShowAllWarning(context, showWarningAgain.isChecked());

                        showMoreCommandsDialog(holder, context);
                    }

                }).create()
                .show();
    }

    /**
     * Call this to show the "More" dialog for the passed {@link FileHolder}.
     *
     * @param context Always useful, isn't it?
     */
    private static void showMoreCommandsDialog(FileHolder holder, final Context context) {
        final Uri data = Uri.fromFile(holder.getFile());
        final Intent intent = new Intent();
        intent.setDataAndType(data, holder.getMimeType());

        if (holder.getMimeType() != null) {
            // Add additional options for the MIME type of the selected file.
            PackageManager pm = context.getPackageManager();
            final List<ResolveInfo> lri = pm.queryIntentActivityOptions(
                    new ComponentName(context, FileManagerActivity.class),
                    null, intent, 0);
            final int N = lri != null ? lri.size() : 0;

            // Create name list for menu item.
            final List<CharSequence> items = new ArrayList<>();
            /* Some of the options don't go to the list hence we have to remove them
             * to keep the lri correspond with the menu items. In the addition, we have
             * to remove them after the first iteration, otherwise the iteration breaks.
             */
            List<ResolveInfo> toRemove = new ArrayList<>();
            for (int i = 0; i < N; i++) {
                final ResolveInfo ri = lri.get(i);
                Intent rintent = new Intent(intent);
                rintent.setComponent(
                        new ComponentName(
                                ri.activityInfo.applicationInfo.packageName,
                                ri.activityInfo.name));
                ActivityInfo info = rintent.resolveActivityInfo(pm, 0);
                String permission = info.permission;
                if (info.exported && (permission == null || context.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED))
                    items.add(ri.loadLabel(pm));
                else
                    toRemove.add(ri);
            }

            for (ResolveInfo ri : toRemove) {
                lri.remove(ri);
            }

            new AlertDialog.Builder(context)
                    .setTitle(holder.getName())
                    .setIcon(holder.getIcon())
                    .setItems(items.toArray(new CharSequence[0]),
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int item) {
                                    final ResolveInfo ri = lri.get(item);
                                    Intent rintent = new Intent(intent)
                                            .setComponent(new ComponentName(
                                                    ri.activityInfo.applicationInfo.packageName,
                                                    ri.activityInfo.name));
                                    context.startActivity(rintent);
                                }
                            }).create()
                    .show();
        }
    }
}